package net.p2pcdn.http;

import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Getter
@Setter
public class PoolHttpClientService {
    private static Logger _logger = LoggerFactory.getLogger(PoolHttpClientService.class);
    /**
     * * 最大连接数1024      
     */
    private static int DEFAULT_MAX_CONNECTIONS = 1024;
    /**
     * 向服务端请求超时时间设置(单位:毫秒)
     */
    private static int DEFAULT_CONNECTION_TIMEOUT = 5 * 1000;
    /**
     * * 服务端响应超时时间设置(单位:毫秒)  
     */
    private static int DEFAULT_SOCKET_TIMEOUT = 3 * 1000;
    private static int DEFAULT_IDLE_CONNECTION_TIME = 1000 * 60 * 5;
    public static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = -1;
    public static final boolean DEFAULT_USE_REAPER = true;
    public static final int DEFAULT_VALIDATE_AFTER_INACTIVITY = 2 * 1000;

    private int connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
    private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
    private int socketTimeout = DEFAULT_CONNECTION_TIMEOUT;
    private int maxConnections = DEFAULT_MAX_CONNECTIONS;
    private boolean useReaper = DEFAULT_USE_REAPER;
    private long idleConnectionTime = DEFAULT_IDLE_CONNECTION_TIME;
    private String sslCertPath;
    private String sslSecret;

    public int getValidateAfterInactivity() {
        return DEFAULT_VALIDATE_AFTER_INACTIVITY;
    }

    protected RequestConfig requestConfig;

    /**
     * * 构造函数      
     */
    private PoolHttpClientService() {
    }

    /**
     * 连接池管理对象      
     */
    protected HttpClientConnectionManager connectionManager;

    protected CloseableHttpClient httpClient;

    /**
     * *	功能描述: <br>
     * *	初始化连接池管理对象    
     * * @see [相关类/方法](可选)      
     * *	@since
     * [产品/模块版本](可选)      
     */
    public synchronized void init() {
        RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
        requestConfigBuilder.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT);
        requestConfigBuilder.setSocketTimeout(DEFAULT_SOCKET_TIMEOUT);
        requestConfigBuilder.setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT);
        this.connectionManager = createHttpClientConnectionManager();
        this.httpClient = createHttpClient(this.connectionManager);
        this.requestConfig = requestConfigBuilder.build();
    }


    protected HttpClientConnectionManager createHttpClientConnectionManager() {
        SSLContext sslContext = null;
        InputStream inputStream = null;
        try {
            if (StringUtils.isNotBlank(sslCertPath)) {
                SSLContextBuilder sslContextBuilder = org.apache.http.ssl.SSLContexts.custom();
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                inputStream = new FileInputStream(sslCertPath);
                keyStore.load(inputStream, sslSecret.toCharArray());
                sslContextBuilder.loadKeyMaterial(keyStore, sslSecret.toCharArray());
                sslContext = sslContextBuilder.setProtocol("TLS").build();
            }
            if (sslContext == null) {
                sslContext = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true).build();
            }
        } catch (Exception e) {
            _logger.error(e.getMessage(), e);
        }
        @SuppressWarnings("deprecation")
        HostnameVerifier hostnameVerifier = SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
        SSLConnectionSocketFactory sslSocketFactory =
                new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"}, null, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connectionManager.setDefaultMaxPerRoute(this.getMaxConnections());
        connectionManager.setMaxTotal(this.getMaxConnections());
        connectionManager.setValidateAfterInactivity(this.getValidateAfterInactivity());
        connectionManager.setDefaultSocketConfig(SocketConfig.custom().
                setSoTimeout(this.getSocketTimeout()).setTcpNoDelay(true).build());
        if (this.isUseReaper()) {
            IdleConnectionReaper.setIdleConnectionTime(this.getIdleConnectionTime());
            IdleConnectionReaper.registerConnectionManager(connectionManager);
        }
        return connectionManager;
    }


    protected CloseableHttpClient createHttpClient(HttpClientConnectionManager connectionManager) {
        return HttpClients.custom().setConnectionManager(connectionManager)
                .disableContentCompression()
                .disableAutomaticRetries()
                .build();
    }

    public void destory() {
        if (connectionManager != null) {
            IdleConnectionReaper.removeConnectionManager(this.connectionManager);
            connectionManager.shutdown();
        }
    }

    /**
     * Http post请求      
     *
     * @param url  请求地址    
     * @param json 请求参数(如果为null,则表示不请求参数)
     *             return 返回结果      
     */
    public CloseableHttpResponse post(String url, String json, ContentType contentType) {
        _logger.info(url);
        _logger.info(json);
        CloseableHttpResponse response = null;
        HttpPost post = null;
        try {
            // 设置代理
            post = new HttpPost(url);
            post.setConfig(requestConfig);
            if (json != null) {
                post.setEntity(new StringEntity(json, (contentType == null ? ContentType.APPLICATION_JSON : contentType)));
            }
            _logger.info("start post to request " + System.currentTimeMillis());
            response = httpClient.execute(post);
            _logger.info("end post to request " + System.currentTimeMillis());
            return response;
        } catch (Exception e) {
            if (e instanceof SocketTimeoutException) {
                // 服务器请求超时
                _logger.error("server request time out");
            } else if (e instanceof ConnectTimeoutException) {
                // 服务器响应超时(已经请求了)
                _logger.error("server response time out");
            }
            _logger.error(e.getMessage(), e);
        }
        return response;
    }

    /**
     * Http get请求      
     *
     * @param url 请求地址    
     *            return 返回结果      
     */
    public CloseableHttpResponse get(String url) {
        _logger.info("start request url  " + url);
        _logger.info("start request url  " + System.currentTimeMillis());
        CloseableHttpResponse response = null;
        HttpGet get = null;
        try {
            get = new HttpGet(url);
            get.setConfig(this.requestConfig);
            response = httpClient.execute(get);
            _logger.info("end request " + System.currentTimeMillis());
            return response;
        } catch (Exception e) {
            if (e instanceof SocketTimeoutException) {
                // 服务器请求超时
                _logger.error("server request time out");
            } else if (e instanceof ConnectTimeoutException) {
                // 服务器响应超时(已经请求了)
                _logger.error("server response time out");
            }
            _logger.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 文件下载
     * Http get请求      
     *
     * @param url 请求地址    
     *            return 返回结果      
     */
    public CloseableHttpResponse downloadFile(String url) {
        _logger.info("start request url  " + url);
        _logger.info("start request url  " + System.currentTimeMillis());
        CloseableHttpResponse response = null;
        HttpGet get = null;
        try {
            get = new HttpGet(url);
            get.setConfig(this.requestConfig);
            response = httpClient.execute(get);
            _logger.info("end request " + System.currentTimeMillis());
            return response;
        } catch (Exception e) {
            if (e instanceof SocketTimeoutException) {
                // 服务器请求超时
                _logger.error("server request time out");
            } else if (e instanceof ConnectTimeoutException) {
                // 服务器响应超时(已经请求了)
                _logger.error("server response time out");
            }
            _logger.error(e.getMessage(), e);
        }
        return null;
    }


    public CloseableHttpResponse uploadFile(String url, String filepath) {
        CloseableHttpResponse response = null;
        HttpPost post = null;
        try {
            post = new HttpPost(url);
            File src = new File(filepath);
            FileEntity fileEntity = new FileEntity(src);
            post.setEntity(fileEntity);
            response = httpClient.execute(post);
            return response;
        } catch (Exception e) {
            _logger.error(e.getMessage(), e);
        }
        return null;
    }


    /**
     * Http post请求      
     *
     * @param url 请求地址    
     *            请求参数(如果为null,则表示不请求参数)
     *            return 返回结果      
     */
    public CloseableHttpResponse post(String url, Map<String, String> params) {
        _logger.info(url);
        CloseableHttpResponse response = null;
        HttpPost post = null;
        try {
            // 设置代理
            post = new HttpPost(url);
            post.setConfig(requestConfig);
            //设置参数
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            for (String name : params.keySet()) {
                String value = params.get(name);
                nvps.add(new BasicNameValuePair(name, value));
            }
            post.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8));
            _logger.info("start post to request " + System.currentTimeMillis());
            response = httpClient.execute(post);
            _logger.info("end post to request " + System.currentTimeMillis());
            return response;
        } catch (Exception e) {
            if (e instanceof SocketTimeoutException) {
                // 服务器请求超时
                _logger.error("server request time out");
            } else if (e instanceof ConnectTimeoutException) {
                // 服务器响应超时(已经请求了)
                _logger.error("server response time out");
            }
            _logger.error(e.getMessage(), e);
        }
        return response;
    }

    public String postBody(String url, Map<String, String> params, Map<String, String> headers) {
        _logger.info(url);
        HttpPost method = new HttpPost(url);
        List<NameValuePair> list = new ArrayList<>();
        for (Map.Entry map : params.entrySet()) {
            list.add(new BasicNameValuePair(map.getKey().toString(), map.getValue().toString()));
        }
        for (Map.Entry map : headers.entrySet())
            method.setHeader(map.getKey().toString(), map.getValue().toString());

        method.setEntity(new StringEntity(JSON.toJSONString(list), ContentType.APPLICATION_JSON));
        try {
            CloseableHttpResponse response = httpClient.execute(method);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                String respstr = EntityUtils.toString(response.getEntity());
                _logger.debug(respstr);
                return respstr;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Http post请求      
     *
     * @param url 请求地址    
     *            请求参数(如果为null,则表示不请求参数)
     *            return 返回结果      
     */
    public CloseableHttpResponse post(String url, Map<String, String> params, Map<String, String> headers) {
        _logger.info(url);
        CloseableHttpResponse response = null;
        HttpPost post = null;
        try {
            // 设置代理
            post = new HttpPost(url);
            post.setConfig(requestConfig);
            //设置参数
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            for (String name : params.keySet()) {
                String value = params.get(name);
                nvps.add(new BasicNameValuePair(name, value));
            }
            for (Map.Entry map : headers.entrySet()) {
                post.addHeader(map.getKey().toString(), map.getValue().toString());
            }
            post.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8));
            _logger.info("start post to request " + System.currentTimeMillis());
            response = httpClient.execute(post);
            _logger.info("end post to request " + System.currentTimeMillis());
            return response;
        } catch (Exception e) {
            if (e instanceof SocketTimeoutException) {
                // 服务器请求超时
                _logger.error("server request time out");
            } else if (e instanceof ConnectTimeoutException) {
                // 服务器响应超时(已经请求了)
                _logger.error("server response time out");
            }
            _logger.error(e.getMessage(), e);
        }
        return response;
    }
}